package edu.stu.drawtext.component

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PathMeasure
import android.graphics.PointF
import android.text.TextPaint
import android.util.ArrayMap
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.View
import androidx.annotation.ColorInt
import kotlin.math.abs
import kotlin.math.min

/**
 * 自定义 View 用于将文字转换为轮廓点
 */
class FontPathToPointsView : View {

    // 构造函数
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    // 绘制文字的 Paint 对象
    private lateinit var mTextPaint: TextPaint
    // 显示度量对象
    private lateinit var mDM: DisplayMetrics
    // 要绘制的文字内容
    private val text: String = "母亲节快乐"
    // 文字的宽度
    private var measureTextWidth = 0f
    // HSL 颜色模型数组
    private var hslColor = FloatArray(3)
    // 存储文字轮廓点的 Map 对象
    private val textPoints: MutableMap<String, FontText> = ArrayMap()
    // 字体度量对象
    private val fm = Paint.FontMetrics()
    // 绘制点的 Paint 对象
    private lateinit var mPaint: TextPaint

    // 初始化方法，设置绘制文字的 Paint 对象
    private fun initPaint() {
        mDM = resources.displayMetrics
        // 绘制文字的 Paint 对象
        mTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
            isAntiAlias = true
            isDither = true
            strokeCap = Paint.Cap.ROUND
            style = Paint.Style.FILL
            textSize = sp2px(50f)
        }
        // 绘制点的 Paint 对象
        mPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
            isAntiAlias = true
            isDither = true
            strokeWidth = 5f
        }
    }

    // 初始化
    init {
        initPaint()
    }

    // View 尺寸变化时调用，用于清空 textPoints
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        textPoints.clear()
    }

    // 绘制文字轮廓点
    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        // 如果 textPoints 为空，根据文字内容初始化 textPoints
        if (textPoints.isEmpty()) {
            measureTextWidth = mTextPaint.measureText(text)
            for (i in text.indices) {
                val word = text.substring(i, i + 1)
                textPoints[word] = FontText(i, textPathToPoints(word, mTextPaint), randomColor())
            }
        }
        // 如果 textPoints 仍为空，直接返回
        if (textPoints.isEmpty()) {
            return
        }
        // 绘制文字轮廓点
        val height = height
        val width = width
        val halfOfTextWidth = measureTextWidth / 2f
        val baseline = getTextPaintBaseline(mTextPaint)
        val count = canvas.save()
        canvas.translate(width / 2f, height / 2f)
        val spanSize = measureTextWidth / textPoints.size
        var finishCount = 0
        // 遍历 textPoints，绘制每个文字的轮廓点
        for ((_, textPoint) in textPoints) {
            var size = textPoint.currentSize
            val pointSize = textPoint.pointFS.size
            val offset = textPoint.index * spanSize

            mPaint.color = textPoint.color
            for (i in 0 until size) {
                val pointF = textPoint.pointFS[i]
                canvas.drawPoint(
                    pointF.x - halfOfTextWidth + offset, pointF.y + baseline,
                    mPaint
                )
            }

            size++
            textPoint.currentSize = min(size.toDouble(), pointSize.toDouble()).toInt()
            if (textPoint.currentSize == pointSize) {
                finishCount++
            }
        }
        canvas.restoreToCount(count)

        // 如果所有文字都绘制完成，1s 后重新绘制
        if (finishCount == textPoints.size) {
            for ((_, value) in textPoints) {
                value.currentSize = 0
            }
            postInvalidateDelayed(1000)
        } else {
            postInvalidateDelayed(16)
        }
    }

    // dp 转 px
    fun dp2px(dp: Float): Float {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDM)
    }

    // sp 转 px
    private fun sp2px(dp: Float): Float {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, dp, mDM)
    }

    // 获取文字绘制的基线
    private fun getTextPaintBaseline(p: Paint?): Float {
        p!!.getFontMetrics(fm)
        val fontMetrics = fm
        return (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent
    }

    // 内部类，存储文字信息
    internal class FontText(
        // 当前文字在 Text 中的索引
        var index: Int,
        // 点位
        var pointFS: List<PointF>,
        // 颜色
        var color: Int
    ) {
        // 用户控制绘制到什么位置
        var currentSize = 0
    }

    // 随机生成颜色
    private fun randomColor(): Int {
        hslColor[0] = (Math.random() * 360).toFloat()
        hslColor[1] = 0.5f
        hslColor[2] = 0.5f
        return HSLToColor(hslColor)
    }

    // 将文字转换为轮廓点
    private fun textPathToPoints(text: String, paint: TextPaint): List<PointF> {
        val fontPath = Path()
        paint.getTextPath(text, 0, text.length, 0f, paint.fontSpacing, fontPath)
        fontPath.close()
        val pathMeasure = PathMeasure(fontPath, false)
        val points: MutableList<PointF> = ArrayList()
        val pos = FloatArray(2)
        do {
            var distance = 0f
            while (distance < pathMeasure.length) {
                distance += 5f
                pathMeasure.getPosTan(distance, pos, null)
                points.add(PointF(pos[0], pos[1]))
            }
        } while (pathMeasure.nextContour())
        return points
    }

    // HSL 转颜色值
    @ColorInt
    fun HSLToColor(hsl: FloatArray): Int {
        val h = hsl[0]
        val s = hsl[1]
        val l = hsl[2]
        val c = ((1f - abs((2 * l - 1f).toDouble())) * s).toFloat()
        val m = l - 0.5f * c
        val x = (c * (1f - abs((h / 60f % 2f - 1f).toDouble()))).toFloat()
        val hueSegment = h.toInt() / 60
        var r = 0
        var g = 0
        var b = 0
        when (hueSegment) {
            0 -> {
                r = Math.round(255 * (c + m))
                g = Math.round(255 * (x + m))
                b = Math.round(255 * m)
            }

            1 -> {
                r = Math.round(255 * (x + m))
                g = Math.round(255 * (c + m))
                b = Math.round(255 * m)
            }

            2 -> {
                r = Math.round(255 * m)
                g = Math.round(255 * (c + m))
                b = Math.round(255 * (x + m))
            }

            3 -> {
                r = Math.round(255 * m)
                g = Math.round(255 * (x + m))
                b = Math.round(255 * (c + m))
            }

            4 -> {
                r = Math.round(255 * (x + m))
                g = Math.round(255 * m)
                b = Math.round(255 * (c + m))
            }

            5, 6 -> {
                r = Math.round(255 * (c + m))
                g = Math.round(255 * m)
                b = Math.round(255 * (x + m))
            }
        }
        r = constrain(r, 0, 255)
        g = constrain(g, 0, 255)
        b = constrain(b, 0, 255)
        return Color.rgb(r, g, b)
    }

    // 限制颜色值在指定范围内
    private fun constrain(amount: Int, low: Int, high: Int): Int {
        return if (amount < low) low else min(amount.toDouble(), high.toDouble()).toInt()
    }
}
